package com.fagertveit.android.game.framework.gl;

import java.io.IOException;
import java.io.InputStream;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.opengl.GLUtils;
import android.util.Log;

import com.fagertveit.android.game.framework.FileIO;
import com.fagertveit.android.game.framework.impl.GLGame;
import com.fagertveit.android.game.framework.impl.GLGraphics;

public class Image {
	GLGraphics glGraphics;
	FileIO fileIO;
	String fileName;
	int textureId;
	int minFilter;
	int magFilter;
	int width;
	int height;
	int glWidth;
	int glHeight;
	float u1, v1, u2, v2;
	final float[] verticesBuffer;
	final Vertices vertices;
	
	public Image(GLGame glGame, String fileName) {
		this.glGraphics = glGame.getGLGraphics();
		this.fileIO = glGame.getFileIO();
		this.fileName = fileName;
		this.verticesBuffer = new float[16];
		this.vertices = new Vertices(glGraphics, 32, 64, false, true);
		
		short[] indices = new short[6];
		
		indices[0] = (short)(0);
		indices[1] = (short)(1);
		indices[2] = (short)(2);
		indices[3] = (short)(2);
		indices[4] = (short)(3);
		indices[5] = (short)(0);
			
		vertices.setIndices(indices, 0, indices.length);
	}
	
	public void Render(GLGraphics glGraphics, float x, float y) {
		GL10 gl = glGraphics.getGL();
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
		
		float x1 = x;
		float y1 = y;
		float x2 = x + width;
		float y2 = y + height;
		
		verticesBuffer[0] = x1;
		verticesBuffer[1] = y1;
		verticesBuffer[2] = u1;
		verticesBuffer[3] = v2;
		
		verticesBuffer[4] = x2;
		verticesBuffer[5] = y1;
		verticesBuffer[6] = u2;
		verticesBuffer[7] = v2;
		
		verticesBuffer[8] = x2;
		verticesBuffer[9] = y2;
		verticesBuffer[10] = u2;
		verticesBuffer[11] = v1;
		
		verticesBuffer[12] = x1;
		verticesBuffer[13] = y2;
		verticesBuffer[14] = u1;
		verticesBuffer[15] = v1;

		vertices.setVertices(verticesBuffer, 0, 16);
		vertices.bind();
		vertices.draw(GL10.GL_TRIANGLES, 0, 6);
		vertices.unbind();
	}
	
	public void load() {
		GL10 gl = glGraphics.getGL();
		int[] textureIds = new int[1];
		gl.glGenTextures(1, textureIds, 0);
		textureId = textureIds[0];
		
		Log.d("Image", "Starting the loading of Image");
		InputStream in = null;
		try {
			in = fileIO.readAsset(fileName);
			Bitmap bitmap = BitmapFactory.decodeStream(in);
			width = bitmap.getWidth();
			height = bitmap.getHeight();
			glWidth = getNearestSize(width);
			glHeight = getNearestSize(height);
			Bitmap glBitmap = Bitmap.createBitmap(glWidth, glHeight, bitmap.getConfig());
			glBitmap = copyOntoGlImage(bitmap, glBitmap);
			Log.d("Image", "Bitmap In: " + bitmap.getWidth() + " - " + bitmap.getHeight());
			Log.d("Image", "Bitmap Out: " + glBitmap.getWidth() + " - " + glBitmap.getHeight());
			gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
			GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, glBitmap, 0);
			setFilters(GL10.GL_LINEAR, GL10.GL_LINEAR);
			gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
		} catch(IOException e) {
			throw new RuntimeException("Couldn't load texture '" + fileName + "'", e);
		} finally {
			if(in != null) {
				try { in.close(); } catch (IOException e) { }
			}
		}
		
		this.u1 = 0;
		this.v2 = 0;
		this.u2 = this.u1 + width / glWidth;
		this.v1 = this.v2 + height / glHeight;
	}
	
	public void reload() {
		load();
		GL10 gl = glGraphics.getGL();
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
		setFilters(minFilter, magFilter);
		glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0);
	}
	
	public void setFilters(int minFilter, int magFilter) {
		this.minFilter = minFilter;
		this.magFilter = magFilter;
		GL10 gl = glGraphics.getGL();
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter);
	}
	
	public void dispose() {
		GL10 gl = glGraphics.getGL();
		gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
		int[] textureIds = { textureId };
		gl.glDeleteTextures(1, textureIds, 0);
	}
	
	public static int getNearestSize(int size) {
		if(size < 2) {
			return 2;
		} else if(size <= 4) {
			return 4;
		} else if(size <= 8) {
			return 8;
		} else if(size <= 16) {
			return 16;
		} else if(size <= 32) {
			return 32;
		} else if(size <= 64) {
			return 64;
		} else if(size <= 128) {
			return 128;
		} else if(size <= 256) {
			return 256;
		} else if(size <= 512) {
			return 512;
		} else if(size <= 1024) {
			return 1024;
		} else if( size <= 2048) {
			return 2048;
		} else {
			return 4096;
		}
	}
	
	public static Bitmap copyOntoGlImage(Bitmap bitmap, Bitmap temp) {
		int srcWidth = bitmap.getWidth();
		int srcHeight = bitmap.getHeight();
		
		for(int y = 0; y < srcHeight; y++) {
			for(int x = 0; x < srcWidth; x++) {
				temp.setPixel(x, y, bitmap.getPixel(x, y));
			}
		}
		
		return temp;
	}

	public static Bitmap flip(Bitmap src) {
		// create new matrix for transformation
		Matrix matrix = new Matrix();
		// if vertical
		matrix.preScale(1.0f, -1.0f);
		// return transformed image
		return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
	}
}